/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef DWARF_HH
#define DWARF_HH
//! \addtogroup intromit_core
//@{

#include "util.hh"
#include "string.hh"

#ifndef INCLUDED_LIBDW
#include <elfutils/libdw.h>
#include <dwarf.h>
#else
#include "../../dep/include/libdw.h"
#include "../../dep/include/dwarf.h"
#endif

// DEFINES
#define DW_MAX_P_STACK      32

#define DW_VALUE_NONE         0
#define DW_VALUE_LOCATION     1
#define DW_VALUE_ENCODING     2
#define DW_VALUE_LINKAGE      3
#define DW_VALUE_UPPER_BOUND  4
#define DW_VALUE_VTABLE_LINK  5

#define DW_VALUE2_NONE        0
#define DW_VALUE2_VTABLE_POS  1
#define DW_VALUE2_CONST_VALUE 2

// TYPEDEFS
typedef struct
{
    int code;
    const char *name;
} dwarf_entity;

// CLASSES

//! Metadata specification unit
struct dwarf_base
{
    //! @name Original dwarf relationship
    int off;

    //! @name Base structure
    //@{
    int level;
    int i_parent, child_number;
    int *i_child;
    int n_child, max_child; //@}

    //! @name Attributes
    //@{
    int i_subtype;
    int i_name, i_source, i_namespace, i_attr;
    int type, line;
    int size;
    union
    {
	unsigned all;
	int location;
	int encoding;
	int linkage;
	int upper_bound;
	int vtable_linkage;
    } value;
    union
    {
	unsigned all;
	unsigned vtable_pos;
	int const_value;
    } value2;
    union
    {
	unsigned all;
	struct
	{
	    unsigned value_meaning:4, value_meaning2:4;
	    unsigned declaration:1, artificial:1, implementation:1;
	    unsigned ctor:1, dtor:1, selected:1, virt:1, volat:1;
	    int pattern_match:8; // 0: no match at all, 0x7f: make no recursion,
                                 //>0: make recursion in n indirections, -1: used for other patterns
	};
    } bit;

    //! Manager function
    int i_function; //@}

    //! @name Children
    //@{
    int add_child(int i_child);
    void del_children(); //!< delete all children
    void copy_attribs_from(dwarf_base *orig); //@}
};

//! DWARF debugging info (metadata) manager
class dwarf
{
    // ** CTOR/INIT VARIABLES **

    // elf and dwarf info
    char *elfname;
    int fd;
    Dwarf *dw;

    // base extraction
    dwarf_base root;
    dwarf_base *base;
    int n_base, max_base;
    string_index *doc;

    // navigation
    int pointer_stack[DW_MAX_P_STACK];
    int pointer_i, pointer_max;

    // search
    int search_just_init; // to search also first base

    // inheritance
    int inheritance_level;

    // ** STATUS VARIABLES **

    // ** PRIVATE FUNCTIONS **
    bool analyze_begin();
    bool analyze_complete_declaration_with_definition(int i_base, Dwarf_Off *off, Dwarf_Die *die, Dwarf_Die **diep,
						      Dwarf_Die *stack, int *i_stack, const char *cu_directory,
						      int *&discard, int &n_discard, int &max_discard);
    void analyze_complete_declaration_with_definition_die(Dwarf_Die *die, dwarf_base *b, const char *cu_directory);
    bool analyze_add_location(Dwarf_Die *die, dwarf_base *b);
    bool analyze_add_file_line(Dwarf_Die *die, dwarf_base *b, const char *cu_directory);

    // dump
    void dump_expression(Dwarf_Op *expr, unsigned int n_expr);
    bool dump_cus(Dwarf *dw, FILE *out);
    bool dump_dies(Dwarf *dw, Dwarf_Die *die, int level, int recur, FILE *out);
    unsigned int addr_from_expression(Dwarf_Op *expr, unsigned int n_expr); // 0xffffffff if error
    void analyze_dump_recur(FILE *out, int i, int level, int recur_child, int recur_type);

    // traverse dies
    void traverse_dies_init(Dwarf_Off *off, Dwarf_Die **diep, Dwarf_Die **stack, int *i_stack);
    Dwarf_Die *traverse_dies(Dwarf *dw, Dwarf_Off *off, Dwarf_Die *die, Dwarf_Die **diep, Dwarf_Die *stack, int *i_stack, int tag_type);
    void traverse_dies_finish(Dwarf_Die **stack);

    // operations on bases
    bool equal_bases(int i, int j, int recur_level=0); // recursively compares bases
    void equal_bases_subst(int i, int j, int *subst);
    int analyze_remove_duplicates_recur(int i, dwarf_base *&new_base, int &n_new_base, int &max_new_base, int *subst, int *old_to_new, int level=0, int all=0);
    int search_add_string_check_artificial(char *name);

    // debug and util
    int search_by_offset(int offset);   // dicotomic
    void dump_stack(dwarf_base **stack, int n_stack);
    bool check_base_structure();

    // resolve symbols
    bool find_function_symbol(string_index *symtab, dwarf_base *b, FILE *arch=stdout);
    bool match_prototype_params(char *proto, dwarf_base *b);
    bool match_string_type(char *string_type, dwarf_base *type); // compares a string representing a type with its base representation

    // analyze
    inline int pointNC() {return pointer_stack[pointer_i];}
    inline int pointer() {if(pointer_i<0 || pointer_i>=DW_MAX_P_STACK) {ERROR("p_stack"); return -1;} else return pointer_stack[pointer_i];}
    inline int check_pointer() {if(pointer()<0 || pointNC()>=n_base) ERROR_R0("pointer error"); else return 1;}

    // internal
    void ctor();
    void dtor();

public:
    dwarf();
    ~dwarf();

    //! Dump ELF bases hierarchically
    //! \param elf_filename name of the ELF file to dump
    //! \param out_filename if NULL then stdout is used
    bool dump(char *elf_filename, char *out_filename);

    //! @name Extract dwarf info from elf
    //@{
    bool analyze_begin(char *elf_filename);
    bool analyze_remove_duplicates();
    bool analyze_resolve_imported_declarations_and_typedefs();
    bool analyze_remove_non_matched();
    string_index *analyze_reindex_doc(); // returns old string_index that must be deleted externally
    bool analyze_check_complete_linkage(int verbo=0, char *filename_unresol=NULL, char *filename_symbols=NULL);
    int analyze_dump(char *out_filename=NULL, int from=-1, int initial_level=0, int recur_child=-1, int recur_type=0/* >=0 */);
    int analyze_dump_arch(FILE *arch=stdout, int from=-1, int initial_level=0, int recur_child=-1, int recur_type=0/* >=0 */);
    int analyze_dump_debug(char *out_filename);
    void analyze_dump_stats();
    bool analyze_end(); //@}

    //! @name Dwarf_base tree info and browsing
    //@{
    inline int analyze_get_n_base() {return n_base;}
    inline dwarf_base *analyze_get_base(int x) {return &base[x];}
    inline dwarf_base *analyze_get_bases() {return base;}
    inline int analyze_get_pointer_stack_level() {return pointer_i;}
    int analyze_get_level();
    int analyze_get_n_in_level();
    int analyze_get_n_child();
    bool analyze_move_in_level(int i_inlevel);
    bool analyze_move_next_in_level();
    bool analyze_down(int i_child);
    bool analyze_up();
    bool analyze_goto(int index);
    inline bool analyze_goto_beginning() {return analyze_goto(0);}
    bool analyze_traverse();    // traverse tree in depth
    void analyze_dump_position();
    void analyze_dump_inheritance_tree(); //@}

    //! @name Search functions: search in all current level (including brothers) recursively
    //@{
    bool search_new(); //!< increments pointer_stack
    bool search_next_by_name(char *name);
    bool search_next_by_type(int type);
    bool search_next_by_name_type(char *name, int type);
    bool search_next_class_with_inheritance(char *inheritance);
    void search_end(); //@}

    //! @name Inheritance functions
    //@{
    bool inheritance_new(); // only an inheritance traverse at a time!
    bool inheritance_next(int *level=NULL);
    bool inheritance_end(); //@}

    //! @name Current base info
    //@{
    inline int bp_index() {return pointer();}
    char *bp_name();
    int bp_i_name();
    char *bp_type_name();
    int bp_type();
    char *bp_source_file();
    char *bp_namespace();
    int bp_i_source_file();
    int bp_i_namespace();
    int bp_i_attr();
    int bp_source_line();
    int bp_source_column();
    int bp_byte_size();
    dwarf_base *bp();
    inline int bp_index_from_addr(dwarf_base *b) {return b-base;} //@}

    //! @name Structure/class specific
    //@{
    dwarf_base *bp_class_empty_ctor();
    dwarf_base *bp_class_dtor(); //@}

    //! @name Util
    //@{
    static char *entity_string(const dwarf_entity *list, int n_list, int code, char *buf);
    static char *entity_string(const dwarf_entity *list, int n_list, int code);
    static char *entity_tag_string(int code, char *buf);
    static char *entity_tag_string(int code);
    static char *entity_encoding_string(int code); //@}

    inline string_index *get_doc() {return doc;}
};

//@}
#endif
